package io.gitee.zhangbinhub.acp.boot.websocket.servlet

import io.gitee.zhangbinhub.acp.boot.exceptions.WebSocketException
import io.gitee.zhangbinhub.acp.boot.log.LogAdapter
import io.gitee.zhangbinhub.acp.boot.tools.SslTools
import org.springframework.web.socket.*
import org.springframework.web.socket.client.standard.StandardWebSocketClient
import java.net.URI
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit
import javax.net.ssl.SSLContext

abstract class AcpWebSocketClient
@JvmOverloads constructor(
    logAdapter: LogAdapter,
    @JvmField val clientId: String,
    @JvmField val sslType: String = "SSL"
) : AcpWebSocketHandlerServlet(logAdapter) {
    @JvmOverloads
    @Throws(WebSocketException::class)
    fun connect(
        uri: URI,
        headers: WebSocketHttpHeaders? = null,
        timeOut: Long = 10000,
        timeUnit: TimeUnit = TimeUnit.MILLISECONDS
    ) {
        try {
            close()
            StandardWebSocketClient().let { webSocketClient ->
                webSocketClient.sslContext = if (uri.scheme == "wss") {
                    SSLContext.getInstance(sslType)?.apply {
                        this.init(null, arrayOf(SslTools.getTrustManager()), null)
                    }
                } else {
                    null
                }
                webSocketClient.execute(this, headers, uri).get(timeOut, timeUnit)
            }
        } catch (e: Exception) {
            logAdapter.error(e.message, e)
            close()
            throw WebSocketException("WebSocket client connect failed!")
        }
    }

    fun close() {
        getInstance(clientId)?.session?.close()
    }

    override fun afterConnectionEstablished(session: WebSocketSession) {
        logAdapter.info("WebSocket client [clientId=$clientId] open: ${session.uri}")
        this.session = session
        acpWebSocketInstanceMap[clientId] = this
        sessionOpen(session)
    }

    override fun afterConnectionClosed(session: WebSocketSession, closeStatus: CloseStatus) {
        acpWebSocketInstanceMap.remove(clientId)
        sessionClose(session, closeStatus)
    }

    companion object {
        private val acpWebSocketInstanceMap: ConcurrentHashMap<String, AcpWebSocketClient> =
            ConcurrentHashMap<String, AcpWebSocketClient>()

        @JvmStatic
        fun getInstance(clientId: String): AcpWebSocketClient? = acpWebSocketInstanceMap[clientId]

        @JvmStatic
        fun getInstanceList(): List<AcpWebSocketClient> = acpWebSocketInstanceMap.map { entry -> entry.value }
    }
}